/*
 * Implementation of getprocinfo system call. Takes in a pointer to
 * a proc_info_struct defined in getprocinfo.h and fills it in with
 * information about the given PID or current process (if no PID 
 * specified.
 *
 * Ben Marks, Chris Lekas - CS45 February 2014
 *
 */
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/list.h>

#include <linux/getprocinfo.h>

/* getprocinfo - Extracts information from given pid's task_struct and fills in
 *  proc_info_struct passed in by reference.
 *
 * @param pid - Process ID of process to analyze.
 * @param ps - proc_info_struct to fill passed in by reference from user.
 * @return 0 on success. On failure, sets errno and returns -1.
 */
SYSCALL_DEFINE2( getprocinfo, pid_t, pid, struct proc_info_struct __user *, ps ) 
{
  
  struct task_struct * ts;
  struct proc_info_struct toRet;
  u32 i;
  struct list_head * pos;

  if ( pid < 0 ) { // Invalid process ID argument.
    return -EINVAL;
  }

  if ( ! access_ok( VERIFY_WRITE, ps, sizeof( struct proc_info_struct ) ) ) {
    // Pointer address is invalid.
    return -EFAULT;
  }

  if ( copy_from_user( &toRet, ps, sizeof( struct proc_info_struct ) ) ) {
    // Can't copy from user-space.
    return -EFAULT;
  }

  
  rcu_read_lock(); // Make sure task_struct doesn't change under us.

  if ( pid ) { // Get task_struct of given pid.
    ts = find_task_by_vpid( pid );
  }
  else { // Get task_struct of this process.
    ts = get_current();
  }

  if ( ! ts ) { // Error getting task_struct.
    rcu_read_unlock();
    return -ESRCH;
  }

  // If we get here we have valid parameters

  // Fill in struct proc_info_struct
  toRet.pid              = ts->pid;
  toRet.parn_pid         = ts->real_parent->pid;
  toRet.gid              = ts->cred->gid;
  toRet.user_time        = ts->utime;
  toRet.sys_time         = ts->stime;
  toRet.state            = ts->state;
  toRet.sched_avg_running= ts->se.avg_running;
  toRet.time_slice       = ts->rt.time_slice;
  toRet.policy           = ts->policy;
  toRet.num_cxs          = ts->nvcsw + ts->nivcsw;

  // Calculate number of children
  i = 0;
  list_for_each( pos, &(ts->children)) {
    i++;
  }
  toRet.num_children = i;

  // Copy program name to struct proc_info_struct (length defined in header
  //  file as 16 chars).
  memcpy( toRet.prog, ts->comm, 15 * sizeof(char) );
  toRet.prog[15] = '\0';
  
  rcu_read_unlock(); // Done with task_struct.

  if ( copy_to_user( ps, &toRet, sizeof( struct proc_info_struct ) ) ) {
     // Can't copy back to user-space.
    return -EFAULT;
  }
  
  return 0;



}
